Skip to content

feat(packaging): load JavaScript from app.asar and unpack only native files - #647

Merged
yaojin3616 merged 2 commits into
fix/node-pty-unpacked-helperfrom
feat/asar-native-unpack
Oct 2, 2026
Merged

yaojin3616 merged 2 commits into
fix/node-pty-unpacked-helperfrom
feat/asar-native-unpack

Conversation

@yaojin3616

@yaojin3616 yaojin3616 commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Stacked on #646 (base: fix/node-pty-unpacked-helper). It aligns the package layout with upstream deepseek-harness apps/desktop.

Why

Since #646, every consumer of the bundled packages runs on the Electron runtime: the main process, the Harness utility process on macOS, and the Helper or executable in Node mode for package commands and shims. That runtime reads app.asar. Unpacking all of node_modules therefore bought nothing, and it caused two problems:

Changes

  • asarUnpack keeps only what the OS loads or executes: native addons and libraries, node-pty spawn-helper, ripgrep, the LibreOffice engine and sherpa-onnx platform packages, and the PPT runtime.
  • Runtime root: removed runtimePackageRoot; the bundled runtime root is app.getAppPath() (app.asar when packaged).
  • Office engine: build/office-engine-resolution.mjs is adapted from upstream apps/desktop-host/src/office-engine.ts. It resolves @deepseek-ai/libreoffice-kit-<platform>-* to app.asar.unpacked, because libreoffice-kit derives the engine executable from that package's resolved path, and through the archive the executable fails stat/spawn ("executable is not executable"). It is registered in harness-node-entry and is a no-op outside an app.asar layout. Like upstream, it canonicalizes the archive path before the prefix comparison. Otherwise a symlinked or junctioned install, Windows 8.3 names or drive-letter case would leave the engine inside the archive.
  • node-pty patch removed: upstream node-pty's own app.asar → app.asar.unpacked mapping is correct in this layout. Upstream relies on the same behaviour.
  • afterPack (scripts/after-pack.cjs):
    • verifies the PPT runtime through app.asar on the packaged Helper;
    • new scripts/verify-asar-unpack.cjs fails packaging when a Mach-O, ELF or PE file is packed inline.
  • Windows release smoke loads koffi and pnpm through app.asar.
  • Docs (release runbook) and tests updated.

Startup stages touched (AGENTS.md table):

  • Backend loading: Harness entry and host anchor now point inside app.asar.
  • Client discovery: same anchor.
  • Both ordinary Profiles and Safe Mode use the installation anchor in app.asar. Profiles and generations hold no links into the installation directory.

Also included: Safe Mode sidebar exit

With blocking compatibility findings, the sidebar "Exit Safe Mode" button reopened the already-open manager and returned, so the click did nothing visible. The manager's own restart button asked for confirmation and exited.

The sidebar button now asks the same question (shared safeModeExitConfirmation). "Exit anyway" leaves Safe Mode; "Manage plugins" opens the manager. When the manager is open and waiting, the exit goes through its restart action.

Checked: unit tests in test/safe-mode.test.ts. Not yet clicked in the real Safe Mode UI with blocking findings.

Checks

  • npm test: 180 files / 1606 tests. npm run typecheck and git diff --check pass.
  • New regression tests:
    • node-pty-asar-layout spawns a PTY with unpatched node-pty loaded from app.asar and natives in .unpacked;
    • office-engine-resolution resolves the engine on the Electron runtime, with and without the hook, and through a linked install directory;
    • verify-asar-unpack rejects an inline Mach-O and accepts it once unpacked.
  • Unsigned package:dev:dir on macOS arm64 passes both afterPack gates. Unpacked files went from 21,212 (549 MB) to 1,596 (235 MB); app.asar is 281 MB.
  • The packaged app was run with an isolated userData and HOME:
    • Harness loads from app.asar in the utility process and is ready in about 1.6 s. The UI mounts, including the PPT plugin.
    • The sidebar terminal is interactive: echo returns /bin/zsh arm64 on unpatched node-pty.
    • In a utility process, as Harness runs: node-pty, ripgrep and pnpm work, and LibreOffice converts an ODT to PDF through the asar-loaded worker. The conversion fails without the hook.
    • The .desktop-bin pnpm shim runs the app.asar pnpm.
    • The main process installed a real generation using the installer and pnpm from app.asar.
    • Safe Mode starts: Harness is ready from the installation anchor and the UI mounts.

Not verified

  • Windows packaging and installer, including install time and file count. Also unverified there: the koffi directory-picker worker loading from app.asar, which the CI smoke does not cover.
  • x64 macOS, a signed and notarized build.
  • Market install of a plugin through the real UI, a local symlinked plugin, and an upgrade from an installed previous release.

🤖 Generated with Claude Code

yaojin3616 and others added 2 commits October 1, 2026 23:32
… files

Align the package layout with upstream deepseek-harness apps/desktop.
Since #646 every consumer of the bundled packages runs on the Electron
runtime (main, utility process, Helper in Node mode), which reads
app.asar, so unpacking all of node_modules bought nothing: about 21k
loose files that slowed installs and put "app.asar" in physical paths
for dependencies' path heuristics to trip over.

- asarUnpack keeps only native addons/libraries, spawn-helper, ripgrep,
  the LibreOffice engine and sherpa-onnx platform packages and the PPT
  runtime (macOS arm64: 1.6k files / 235 MB unpacked, was 21k / 549 MB).
- Remove runtimePackageRoot; the bundled runtime root is app.getAppPath().
- build/office-engine-resolution.mjs (adapted from upstream
  desktop-host office-engine.ts) resolves the LibreOffice engine package
  to app.asar.unpacked so the OS can spawn its executable; registered in
  harness-node-entry.
- Drop the node-pty patch: upstream node-pty maps app.asar to
  app.asar.unpacked itself, which is correct in this layout.
- afterPack (scripts/after-pack.cjs) now verifies the PPT runtime through
  app.asar and fails when a Mach-O/ELF/PE file is packed inline.
- Windows release smoke loads koffi and pnpm through app.asar.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g findings

With blocking compatibility findings the sidebar button reopened the
Safe Mode manager and returned. When the manager was already open the
click had no visible effect, while the manager's own restart button asked
for confirmation and exited.

Ask the same question as the manager's restart button (shared
safeModeExitConfirmation / safeModeBlockingGroupCount): "Exit anyway"
leaves Safe Mode, "Manage plugins" opens the manager. When the manager is
open and waiting, the exit is handed to its restart action so relaunch,
the unresolved-findings note and a fall-back into Safe Mode are handled in
one place.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@yaojin3616
yaojin3616 merged commit 0d94e7e into fix/node-pty-unpacked-helper Oct 2, 2026
@yaojin3616
yaojin3616 deleted the feat/asar-native-unpack branch October 2, 2026 07:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant